/**
 * @file algomgr.c
 * @author your name (you@domain.com)
 * @brief 任务管理模块
 *        1.目前任务管理只能开启一个算法，algotab硬编码了所有算法对象。根据配置启动对应算法
 *        2.任务可设置debug模式，进入该模式可以运行一次子任务的debug函数   
 * @version 0.1
 * @date 2020-02-27
 * 
 * @copyright Copyright (c) 2020
 * 
 */
#include "algo_mgr.h"

#include <stdio.h> // 包含NULL定义
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <setjmp.h> // setjmp longjmp jmb_buf

#include "adlist.h"

#include "algo_strickle.h"
#include "algo_volume.h"
#include "algo_spray.h"
#include "algo_tarpa.h"
#include "algo_belt.h"
#include "algo_debris.h"

#include "gige.h"
#include "sensor.h"
#include "util.h"
#include "modbus-srv.h"

#define NAME_MAX_LEN 256

// 异常处理
jmp_buf jmpb;
#define _try if(!setjmp(jmpb))
#define _catch else
#define _throw longjmp(jmpb,1)


struct algo_mgr {
    pthread_t tid;     // 线程id
    int run;           // 线程运行标志 0停止 1运行
    int debug;         // debug模式使能 0取消 1使能 
    algo_info* algo;   // 当前算法对象
    #if 0
    algo_info* task; // 当前运行任务
    list* tasklist;      // 子任务链表
    #endif
} algomgr;



static algo_info* _find_algo(const char* name);
static void* _loop(void* arg);

//TODO 改为动态注册
algo_info algotable[10] =  {
    {
        (char*)"appEventStrickle",
        init_strickle, 
        compute_strickle,
        debug_strickle, 
        analysis_strickle,
        response_strickle, 
        destroy_strickle, 
        -1, 
        1,
        NULL,0
    },
    {
        (char*)"appEventAntiBlocking",
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        -1,
        2,
        NULL,0
    },
    {(char*)"appEventMTRough", NULL, NULL, NULL, NULL,NULL, NULL,-1,4,NULL,0},
    {(char*)"appEventLiquidLevel", NULL, NULL, NULL, NULL,NULL, NULL,-2,8,NULL,0},
    {
        (char*)"appEventVolume", 
        init_volume, 
        compute_volume, 
        NULL, 
        analysis_volume, 
        response_volume, 
        destroy_volume,
        -1,
        16,
        NULL,0
    },
    {
        (char*)"appEventBelt", 
        init_belt, 
        compute_belt, 
        NULL, 
        analysis_belt,
        response_belt, 
        destroy_belt,
        -1,
        32,
        NULL,0
    },
    {(char*)"appEventSlime", NULL, NULL, NULL, NULL,NULL, NULL,-1,64,NULL,0},
    {
        (char*)"appEventDebris", 
        init_debris, 
        compute_debris, 
        NULL, 
        analysis_debris,
        response_debris, 
        destroy_debris,
        -1,
        128,
        NULL,0
    },
    {
        (char*)"appEventSpray", 
        init_spray,
        compute_spray,
        NULL,
        analysis_spray,
        response_spray,
        destroy_spray,
        -1,
        256,
        NULL,0
    },
    {
        (char*)"appEventTarpa",
        init_tarpa,
        compute_tarpa,
        NULL,
        analysis_tarpa,
        response_tarpa,
        destroy_tarpa,
        -1,
        512,
        NULL,0
    }
};


algo_info* _find_algo(const char* name)
{
    for (size_t i=0; i<sizeof(algotable)/sizeof(algo_info); i++) {
        if (strcmp(algotable[i].name, name) == 0) {
            return &algotable[i];
        }
    }
    return NULL;
}

#include "ctrl.h"

void* _belt_clean(void* arg)
{
    __log_mesg("Belt clean thread start.\n");
    long run = *((long*)arg);
    time_t cleantime = time(NULL);
    while (run) {
        if ((time(NULL) - cleantime) < 30*3600) {
            sleep(5);
            continue;
        }
        CLEAN_FORWARD(5);
        CLEAN_BACKWARD(5);
        cleantime = time(NULL);
    }
    __log_mesg("Belt clean thread end.\n");
}

void* _loop(void* arg)
{
    algo_info* algo = (algo_info*)arg;
    if (algo == NULL) return NULL;

    // 苫布算法需开启清扫器线程
    pthread_t workid = 0;
    long workflag = 0;
    if (0 == strcmp(algo->name, "appEventBelt")) {
        workflag = 1;
        if (pthread_create(&workid, NULL, _belt_clean, &workflag) != 0) {
            workflag = 0;
		    __log_mesg("Belt clean thread create failed.\n");
	    }
    }

    if (algo->init(algo)) {
        __log_mesg("init_func failed.\n");
    }

    while (algomgr.run) {
        unsigned char* image = 0;
        int width = 0, height = 0;
        
        // 只有当工业相机连接，且选择工业相机通道，才从工业相机获取图像
        // 否则从监控相机获取图像。
        if (algo->channel==1 && get_gige_connected()) {
            if (gige_get_image(&image, &width, &height)) {
                usleep(2*1000);
                continue;
            }
        } else {
            if (sensor_get_image(&image, &width, &height)) {
                usleep(2*1000);
                continue;
            }
        }

        if (!image) {
            __log_mesg("Request image failed.\n");
            continue;
        }

        if (algomgr.debug && algo->debug ) {
            algo->debug(image,width,height);
            algomgr.debug = 0;
        }

        void* status = NULL;
        void* res = NULL;
        //_try {
            res = algo->compute(algo, image,width,height);
        //} _catch{
        //    __log_mesg("Algo crash.\n");
        //    sensor_free_image(image);
        //    continue;
        //}
        if (res) algo->response(algo, algo->analysis(res));

        //注意： 释放图像数据内存
        sensor_free_image(image);
    }

    __log_mesg("loop done.\n");
    if (workflag) {
        workflag = 0;
        pthread_join(workid, NULL);
    }
    algo->destroy(algo);
    return NULL;
}



int start_task(const char* name, int channel, int* mock_seq, int mock_len)
{
    algomgr.algo = _find_algo(name);
    if (algomgr.algo == NULL) {
        __log_mesg("%s dose not exist.\n", name);
        return EXIT_FAILURE;
    }
    algomgr.algo->channel = channel;
    algomgr.run = 1;
    algomgr.algo->mock_data = mock_seq; 
    algomgr.algo->mock_len = mock_len;
	if (pthread_create(&algomgr.tid, NULL, _loop, algomgr.algo) != 0) {
        memset(&algomgr, 0x00, sizeof(algomgr));
		__log_mesg("Failed to create algo thread.\n");
		return EXIT_FAILURE;
	}

    __log_mesg("%s started.\n", name);
    return EXIT_SUCCESS;
}

void stop_task()
{
    if (algomgr.run) {
        __log_mesg("stop %s.\n", algomgr.algo->name);
        algomgr.run = 0;
        pthread_join(algomgr.tid, NULL);
        memset(&algomgr, 0x00, sizeof(algomgr));
        __log_mesg("stopped.\n");
    }  
}

algo_info* get_task_algo()
{
    return algomgr.algo;
}

void debug_task_once()
{
    algomgr.debug = 1;
}


#if 0
// 改为动态添加
int init_task()
{
    memset(&algomgr, 0x00, sizeof(algo_mgr));
    algomgr.subtask = listCreate();
    listSetFreeMethod(algomgr.subtask, free);
}

int add_task(char* name, 
             init_func init, 
             compute_func compu,
             debug_func debug,
             analysis_func analy,
             response_func resp,
             destroy_func dest,
             int id)
{
    algo_info* algo = (algo_info*)malloc(sizeof(algo_info));
    if (algo == NULL) {
        __log_mesg("malloc error.\n");
    }
    algo->name;
    algo->init = init;
    algo->compute = compu;
    algo->debug = debug;
    algo->analysis = analy;
    algo->response = resp;
    algo->destroy = dest;
    algo->id = id;
    algo->channel = -1;
    listAddNodeTail(algomgr.subtask, algo);
    return 0;
}

void release_task()
{
    listRelease(algomgr.subtask);
    memset(&algomgr, 0x00, sizeof(algo_mgr));
}
#endif